/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// common.c -- misc functions used in client and server

#include "quakedef.h"

extern int static_registered;
extern BOOL com_modified;

// this graphic needs to be in the pak file to use registered features
unsigned short pop[] =
{
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x6600, 0x0000, 0x0000, 0x0000, 0x6600, 0x0000,
	0x0000, 0x0066, 0x0000, 0x0000, 0x0000, 0x0000, 0x0067, 0x0000,
	0x0000, 0x6665, 0x0000, 0x0000, 0x0000, 0x0000, 0x0065, 0x6600,
	0x0063, 0x6561, 0x0000, 0x0000, 0x0000, 0x0000, 0x0061, 0x6563,
	0x0064, 0x6561, 0x0000, 0x0000, 0x0000, 0x0000, 0x0061, 0x6564,
	0x0064, 0x6564, 0x0000, 0x6469, 0x6969, 0x6400, 0x0064, 0x6564,
	0x0063, 0x6568, 0x6200, 0x0064, 0x6864, 0x0000, 0x6268, 0x6563,
	0x0000, 0x6567, 0x6963, 0x0064, 0x6764, 0x0063, 0x6967, 0x6500,
	0x0000, 0x6266, 0x6769, 0x6a68, 0x6768, 0x6a69, 0x6766, 0x6200,
	0x0000, 0x0062, 0x6566, 0x6666, 0x6666, 0x6666, 0x6562, 0x0000,
	0x0000, 0x0000, 0x0062, 0x6364, 0x6664, 0x6362, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0062, 0x6662, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0061, 0x6661, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x6500, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x6400, 0x0000, 0x0000, 0x0000
};


char	com_token[1024];
int		com_argc;
char	**com_argv;

#define CMDLINE_LENGTH	256
char	com_cmdline[CMDLINE_LENGTH];

#define NUM_SAFE_ARGVS  7

static char     *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1];
static char     *argvdummy = " ";

static char     *safeargvs[NUM_SAFE_ARGVS] = {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-dibonly"};

/*
============================================================================

					LIBRARY REPLACEMENT FUNCTIONS

============================================================================
*/

void Q_memset (void *dest, int fill, int count)
{
	int             i;

	if ((((long) dest | count) & 3) == 0)
	{
		count >>= 2;
		fill = fill | (fill << 8) | (fill << 16) | (fill << 24);

		for (i = 0; i < count; i++)
			((int *) dest)[i] = fill;
	}
	else
		for (i = 0; i < count; i++)
			((byte *) dest)[i] = fill;
}

void Q_memcpy (void *dest, void *src, int count)
{
	int             i;

	if ((((long) dest | (long) src | count) & 3) == 0)
	{
		count >>= 2;

		for (i = 0; i < count; i++)
			((int *) dest)[i] = ((int *) src)[i];
	}
	else
		for (i = 0; i < count; i++)
			((byte *) dest)[i] = ((byte *) src)[i];
}

int Q_memcmp (void *m1, void *m2, int count)
{
	while (count)
	{
		count--;

		if (((byte *) m1)[count] != ((byte *) m2)[count])
			return -1;
	}

	return 0;
}

void Q_strcpy (char *dest, char *src)
{
	while (*src)
	{
		*dest++ = *src++;
	}

	*dest++ = 0;
}

void Q_strncpy (char *dest, char *src, int count)
{
	while (*src && count--)
	{
		*dest++ = *src++;
	}

	if (count)
		*dest++ = 0;
}

int Q_strlen (char *str)
{
	int             count;

	count = 0;

	while (str[count])
		count++;

	return count;
}

char *Q_strrchr (char *s, char c)
{
	int len = Q_strlen (s);
	s += len;

	while (len--)
		if (*--s == c) return s;

	return 0;
}

void Q_strcat (char *dest, char *src)
{
	dest += Q_strlen (dest);
	Q_strcpy (dest, src);
}

int Q_strcmp (char *s1, char *s2)
{
	while (1)
	{
		if (*s1 != *s2)
			return -1;              // strings not equal

		if (!*s1)
			return 0;               // strings are equal

		s1++;
		s2++;
	}

	return -1;
}

int Q_strncmp (char *s1, char *s2, int count)
{
	while (1)
	{
		if (!count--)
			return 0;

		if (*s1 != *s2)
			return -1;              // strings not equal

		if (!*s1)
			return 0;               // strings are equal

		s1++;
		s2++;
	}

	return -1;
}

int Q_strncasecmp (char *s1, char *s2, int n)
{
	while (1)
	{
		int c1 = *s1++;
		int c2 = *s2++;

		if (!n--)
			return 0; // strings are equal until end point

		if (c1 != c2)
		{
			if (c1 >= 'a' && c1 <= 'z') c1 -= ('a' - 'A');
			if (c2 >= 'a' && c2 <= 'z') c2 -= ('a' - 'A');

			if (c1 != c2)
				return -1; // strings not equal
		}

		if (!c1)
			return 0; // strings are equal
	}

	return -1;
}

int Q_strcasecmp (char *s1, char *s2)
{
	return Q_strncasecmp (s1, s2, 99999);
}

int Q_atoi (char *str)
{
	int             val;
	int             sign;
	int             c;

	if (*str == '-')
	{
		sign = -1;
		str++;
	}
	else
		sign = 1;

	val = 0;

	// check for hex
	if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
	{
		str += 2;

		while (1)
		{
			c = *str++;

			if (c >= '0' && c <= '9')
				val = (val << 4) + c - '0';
			else if (c >= 'a' && c <= 'f')
				val = (val << 4) + c - 'a' + 10;
			else if (c >= 'A' && c <= 'F')
				val = (val << 4) + c - 'A' + 10;
			else
				return val * sign;
		}
	}

	// check for character
	if (str[0] == '\'')
	{
		return sign * str[1];
	}

	// assume decimal
	while (1)
	{
		c = *str++;

		if (c < '0' || c > '9')
			return val * sign;

		val = val * 10 + c - '0';
	}

	return 0;
}


float Q_atof (char *str)
{
	double			val;
	int             sign;
	int             c;
	int             decimal, total;

	if (*str == '-')
	{
		sign = -1;
		str++;
	}
	else
		sign = 1;

	val = 0;

	// check for hex
	if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
	{
		str += 2;

		while (1)
		{
			c = *str++;

			if (c >= '0' && c <= '9')
				val = (val * 16) + c - '0';
			else if (c >= 'a' && c <= 'f')
				val = (val * 16) + c - 'a' + 10;
			else if (c >= 'A' && c <= 'F')
				val = (val * 16) + c - 'A' + 10;
			else
				return val * sign;
		}
	}

	// check for character
	if (str[0] == '\'')
	{
		return sign * str[1];
	}

	// assume decimal
	decimal = -1;
	total = 0;

	while (1)
	{
		c = *str++;

		if (c == '.')
		{
			decimal = total;
			continue;
		}

		if (c < '0' || c > '9')
			break;

		val = val * 10 + c - '0';
		total++;
	}

	if (decimal == -1)
		return val * sign;

	while (total > decimal)
	{
		val /= 10;
		total--;
	}

	return val * sign;
}

char *Q_strtrim (char *str)
{
	char *end;

	// if the string is empty then don't even bother
	if (!str[0]) return str;

	// Trim leading space
	while (isspace (*str)) str++;

	if (*str == 0)  // All spaces?
		return str;

	// Trim trailing space
	end = str + strlen (str) - 1;
	while (end > str && isspace (*end)) end--;

	// Write new null terminator
	*(end + 1) = 0;

	return str;
}


//============================================================================


// ClearLink is used for new headnodes
void ClearLink (link_t *l)
{
	l->prev = l->next = l;
}

void RemoveLink (link_t *l)
{
	l->next->prev = l->prev;
	l->prev->next = l->next;
}

void InsertLinkBefore (link_t *l, link_t *before)
{
	l->next = before;
	l->prev = before->prev;
	l->prev->next = l;
	l->next->prev = l;
}
void InsertLinkAfter (link_t *l, link_t *after)
{
	l->next = after->next;
	l->prev = after;
	l->prev->next = l;
	l->next->prev = l;
}

/*
============================================================================

					BYTE ORDER FUNCTIONS

============================================================================
*/

BOOL bigendien;

short (*BigShort) (short l);
short (*LittleShort) (short l);
int (*BigLong) (int l);
int (*LittleLong) (int l);
float (*BigFloat) (float l);
float (*LittleFloat) (float l);

short ShortSwap (short l)
{
	byte b1, b2;

	b1 = l & 255;
	b2 = (l >> 8) & 255;

	return (b1 << 8) + b2;
}

short ShortNoSwap (short l)
{
	return l;
}

int LongSwap (int l)
{
	byte b1, b2, b3, b4;

	b1 = l & 255;
	b2 = (l >> 8) & 255;
	b3 = (l >> 16) & 255;
	b4 = (l >> 24) & 255;

	return ((int) b1 << 24) + ((int) b2 << 16) + ((int) b3 << 8) + b4;
}

int LongNoSwap (int l)
{
	return l;
}

float FloatSwap (float f)
{
	union
	{
		float f;
		byte b[4];
	} dat1, dat2;

	dat1.f = f;

	dat2.b[0] = dat1.b[3];
	dat2.b[1] = dat1.b[2];
	dat2.b[2] = dat1.b[1];
	dat2.b[3] = dat1.b[0];

	return dat2.f;
}

float FloatNoSwap (float f)
{
	return f;
}


void Swap_Init (void)
{
	byte swaptest[2] = {1, 0};

	// set the byte swapping variables in a portable manner
	if (*(short *) swaptest == 1)
	{
		bigendien = FALSE;
		BigShort = ShortSwap;
		LittleShort = ShortNoSwap;
		BigLong = LongSwap;
		LittleLong = LongNoSwap;
		BigFloat = FloatSwap;
		LittleFloat = FloatNoSwap;
	}
	else
	{
		bigendien = TRUE;
		BigShort = ShortNoSwap;
		LittleShort = ShortSwap;
		BigLong = LongNoSwap;
		LittleLong = LongSwap;
		BigFloat = FloatNoSwap;
		LittleFloat = FloatSwap;
	}
}


/*
==============================================================================

			MESSAGE IO FUNCTIONS

Handles byte ordering and avoids alignment errors
==============================================================================
*/

// writing functions
void MSG_WriteChar (sizebuf_t *sb, int c)
{
	byte    *buf;

#ifdef PARANOID

	if (c < -128 || c > 127)
		Sys_Error ("MSG_WriteChar: range error");

#endif

	buf = (byte *) SZ_GetSpace (sb, 1);
	buf[0] = c;
}

void MSG_WriteByte (sizebuf_t *sb, int c)
{
	byte    *buf;

#ifdef PARANOID

	if (c < 0 || c > 255)
		Sys_Error ("MSG_WriteByte: range error");

#endif

	buf = (byte *) SZ_GetSpace (sb, 1);
	buf[0] = c;
}

void MSG_WriteShort (sizebuf_t *sb, int c)
{
	byte    *buf;

#ifdef PARANOID

	if (c < ((short) 0x8000) || c > (short) 0x7fff)
		Sys_Error ("MSG_WriteShort: range error");

#endif

	buf = (byte *) SZ_GetSpace (sb, 2);
	buf[0] = c & 0xff;
	buf[1] = c >> 8;
}

void MSG_WriteLong (sizebuf_t *sb, int c)
{
	byte    *buf;

	buf = (byte *) SZ_GetSpace (sb, 4);
	buf[0] = c & 0xff;
	buf[1] = (c >> 8) & 0xff;
	buf[2] = (c >> 16) & 0xff;
	buf[3] = c >> 24;
}

void MSG_WriteFloat (sizebuf_t *sb, float f)
{
	union
	{
		float   f;
		int     l;
	} dat;


	dat.f = f;
	dat.l = LittleLong (dat.l);

	SZ_Write (sb, &dat.l, 4);
}

void MSG_WriteString (sizebuf_t *sb, char *s)
{
	if (!s)
		SZ_Write (sb, "", 1);
	else
		SZ_Write (sb, s, Q_strlen (s) + 1);
}

void MSG_WriteCoord (sizebuf_t *sb, float f)
{
	MSG_WriteShort (sb, QuantizedOrigin (f));
}

void MSG_WriteAngle (sizebuf_t *sb, float f)
{
	MSG_WriteByte (sb, QuantizedAngles (f));
}

void MSG_WriteAngle16 (sizebuf_t *sb, float f)
{
	MSG_WriteShort (sb, Q_rint ((f * 65536.0f) / 360.0f) & 65535);
}


// reading functions
int                     msg_readcount;
BOOL        msg_badread;

void MSG_BeginReading (void)
{
	msg_readcount = 0;
	msg_badread = FALSE;
}

// returns -1 and sets msg_badread if no more characters are available
int MSG_ReadChar (void)
{
	int     c;

	if (msg_readcount + 1 > net_message.cursize)
	{
		msg_badread = TRUE;
		return -1;
	}

	c = (signed char) net_message.data[msg_readcount];
	msg_readcount++;

	return c;
}

int MSG_ReadByte (void)
{
	int     c;

	if (msg_readcount + 1 > net_message.cursize)
	{
		msg_badread = TRUE;
		return -1;
	}

	c = (unsigned char) net_message.data[msg_readcount];
	msg_readcount++;

	return c;
}

int MSG_ReadShort (void)
{
	int     c;

	if (msg_readcount + 2 > net_message.cursize)
	{
		msg_badread = TRUE;
		return -1;
	}

	c = (short) (net_message.data[msg_readcount]
				 + (net_message.data[msg_readcount + 1] << 8));

	msg_readcount += 2;

	return c;
}

int MSG_ReadLong (void)
{
	int     c;

	if (msg_readcount + 4 > net_message.cursize)
	{
		msg_badread = TRUE;
		return -1;
	}

	c = net_message.data[msg_readcount]
		+ (net_message.data[msg_readcount + 1] << 8)
		+ (net_message.data[msg_readcount + 2] << 16)
		+ (net_message.data[msg_readcount + 3] << 24);

	msg_readcount += 4;

	return c;
}

float MSG_ReadFloat (void)
{
	union
	{
		byte    b[4];
		float   f;
		int     l;
	} dat;

	dat.b[0] =      net_message.data[msg_readcount];
	dat.b[1] =      net_message.data[msg_readcount + 1];
	dat.b[2] =      net_message.data[msg_readcount + 2];
	dat.b[3] =      net_message.data[msg_readcount + 3];
	msg_readcount += 4;

	dat.l = LittleLong (dat.l);

	return dat.f;
}

char *MSG_ReadString (void)
{
	static char     string[2048];
	int             l, c;

	l = 0;

	do
	{
		c = MSG_ReadChar ();

		if (c == -1 || c == 0)
			break;

		string[l] = c;
		l++;
	} while (l < sizeof (string) - 1);

	string[l] = 0;

	return string;
}

float MSG_ReadCoord (void)
{
	return MSG_ReadShort () * (1.0 / 8);
}

float MSG_ReadAngle (void)
{
	return MSG_ReadChar () * (360.0 / 256);
}


float MSG_ReadAngle16 (void)
{
	return MSG_ReadShort () * (360.0 / 65536);
}


//===========================================================================

void SZ_Alloc (sizebuf_t *buf, int startsize)
{
	if (startsize < 256)
		startsize = 256;

	buf->data = (byte *) zoneHeap->Alloc (startsize);
	buf->maxsize = startsize;
	buf->cursize = 0;
}


void SZ_Free (sizebuf_t *buf)
{
	buf->cursize = 0;
}

void SZ_Clear (sizebuf_t *buf)
{
	buf->cursize = 0;
}

void *SZ_GetSpace (sizebuf_t *buf, int length)
{
	void *data;

	if (buf->cursize + length > buf->maxsize)
	{
		if (!buf->allowoverflow) Sys_Error ("SZ_GetSpace: overflow without allowoverflow set");
		if (length > buf->maxsize) Sys_Error ("SZ_GetSpace: %i is > full buffer size", length);

		buf->overflowed = TRUE;
		Con_Printf (PRINT_DEFAULT, "SZ_GetSpace: overflow");
		SZ_Clear (buf);
	}

	data = buf->data + buf->cursize;
	buf->cursize += length;

	return data;
}


void SZ_Write (sizebuf_t *buf, void *data, int length)
{
	Q_memcpy (SZ_GetSpace (buf, length), data, length);
}


void SZ_Print (sizebuf_t *buf, char *data)
{
	int len;

	len = Q_strlen (data) + 1;

	// byte * cast to keep VC++ happy
	if (buf->data[buf->cursize - 1])
		Q_memcpy ((byte *) SZ_GetSpace (buf, len), data, len); // no trailing 0
	else Q_memcpy ((byte *) SZ_GetSpace (buf, len - 1) - 1, data, len); // write over trailing 0
}


/*
============
va

does a varargs printf into a temp buffer, so I don't need to have
varargs versions of all text functions.
FIXME: make this buffer size safe someday
============
*/
char *va (char *format, ...)
{
	// scratch is cleared every frame and sized to 64mb so this is OK to do
	va_list argptr;
	char *string = NULL;
	QSCRATCHLOCKER Lock ((void **) &string, __FUNCTION__);

	va_start (argptr, format);
	vsprintf (string, format, argptr);
	va_end (argptr);

	// advance the low mark to account for the size of the string
	qScratch->AdvanceLowMark (strlen (string) + 1);

	return string;
}


/// just for debugging
int memsearch (byte *start, int count, int search)
{
	int i;

	for (i = 0; i < count; i++)
		if (start[i] == search)
			return i;

	return -1;
}


/*
==============
COM_Parse

Parse a token out of a string
==============
*/
char *COM_Parse (char *data)
{
	int c;
	int len;

	len = 0;
	com_token[0] = 0;

	if (!data)
		return NULL;

	// skip whitespace
skipwhite:
	while ((c = *data) <= ' ')
	{
		if (c == 0)
			return NULL;                    // end of file;

		data++;
	}

	// skip // comments
	if (c == '/' && data[1] == '/')
	{
		while (*data && *data != '\n')
			data++;

		goto skipwhite;
	}


	// handle quoted strings specially
	if (c == '\"')
	{
		data++;

		while (1)
		{
			c = *data++;

			if (c == '\"' || !c)
			{
				com_token[len] = 0;
				return data;
			}

			com_token[len] = c;
			len++;
		}
	}

	// parse single characters
	if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':')
	{
		com_token[len] = c;
		len++;
		com_token[len] = 0;
		return data + 1;
	}

	// parse a regular word
	do
	{
		com_token[len] = c;
		data++;
		len++;
		c = *data;

		if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':')
			break;
	} while (c > 32);

	com_token[len] = 0;
	return data;
}


/*
================
COM_CheckParm

Returns the position (1 to argc-1) in the program's argument list
where the given parameter apears, or 0 if not present
================
*/
int COM_CheckParm (char *parm)
{
	int i;

	for (i = 1; i < com_argc; i++)
	{
		if (!com_argv[i])
			continue;               // NEXTSTEP sometimes clears appkit vars.

		if (!Q_strcmp (parm, com_argv[i]))
			return i;
	}

	return 0;
}


/*
================
COM_InitArgv
================
*/
void COM_InitArgv (int argc, char **argv)
{
	BOOL safe;
	int i, j, n;

	// reconstitute the command line for the cmdline externally visible cvar
	n = 0;

	for (j = 0; (j < MAX_NUM_ARGVS) && (j < argc); j++)
	{
		i = 0;

		while (n < (CMDLINE_LENGTH - 1) && argv[j][i])
		{
			com_cmdline[n++] = argv[j][i++];
		}

		if (n < (CMDLINE_LENGTH - 1))
			com_cmdline[n++] = ' ';
		else break;
	}

	com_cmdline[n] = 0;
	safe = FALSE;

	for (com_argc = 0; (com_argc < MAX_NUM_ARGVS) && (com_argc < argc); com_argc++)
	{
		largv[com_argc] = argv[com_argc];

		if (!Q_strcmp ("-safe", argv[com_argc]))
			safe = TRUE;
	}

	if (safe)
	{
		// force all the safe-mode switches. Note that we reserved extra space in
		// case we need to add these, so we don't need an overflow check
		for (i = 0; i < NUM_SAFE_ARGVS; i++)
		{
			largv[com_argc] = safeargvs[i];
			com_argc++;
		}
	}

	largv[com_argc] = argvdummy;
	com_argv = largv;
}


/*
================
COM_CheckRegistered

Looks for the pop.txt file and verifies it.
Sets the "registered" cvar.
Immediately exits out if an alternate game was attempted to be started without
being registered.
================
*/
void COM_CheckRegistered (void)
{
	int h;
	unsigned short  check[128];
	int i;

	FS_OpenFile ("gfx/pop.lmp", &h);
	static_registered = 0;

	if (h == -1)
	{
		Con_Printf (PRINT_DEFAULT, "Playing shareware version.\n");

		if (com_modified)
			Sys_Error ("You must have the registered version to use modified games");

		return;
	}

	Sys_FileRead (h, check, sizeof (check));
	FS_CloseFile (h);

	for (i = 0; i < 128; i++)
		if (pop[i] != (unsigned short) BigShort (check[i]))
			Sys_Error ("Corrupted data file.");

	Cvar_Set (Cvar_Get ("cmdline", "0", CVAR_SERVER), com_cmdline);
	Cvar_SetValue (registered, 1);

	static_registered = 1;

	Con_Printf (PRINT_DEFAULT, "Playing registered version.\n");
}


char *Q_TimeToString (int timeseconds)
{
	int dig = timeseconds / 60;
	int num = timeseconds - dig * 60;

	return va ("%3i:%02i", dig, num);
}


char *Q_TimeToStringUnpadded (int timeseconds)
{
	int dig = timeseconds / 60;
	int num = timeseconds - dig * 60;

	return va ("%i:%02i", dig, num);
}


DWORD NumberOfSetBits (DWORD x)
{
    x = x - ((x >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    x = (x + (x >> 4)) & 0x0F0F0F0F;
    x = x + (x >> 8);
    x = x + (x >> 16);

    return x & 0x0000003F;
}


char *CopyString (const char *in)
{
	char *out = (char *) zoneHeap->Alloc (strlen (in) + 1);
	strcpy (out, in);
	return out;
}


/*
============================

AUTOCOMPLETION LIST

The standard command processor also uses this for faster processing (because it's sorted it can use a
binary search on it rather than a linear walk of linked lists)

============================
*/
clistentry_t cmd_completelist[MAX_COMPLETE_LIST];
int cmd_numcompletelist;
BOOL cmd_complete_dirty = TRUE;

int Cvar_CompleteListSortFunc (clistentry_t *a, clistentry_t *b)
{
	return stricmp (a->name, b->name);
}


void Cmd_CheckList (void)
{
	// sort the completion list if it's dirty
	if (cmd_complete_dirty)
	{
		qsort (cmd_completelist, cmd_numcompletelist, sizeof (clistentry_t), (sortfunc_t) Cvar_CompleteListSortFunc);
		cmd_complete_dirty = FALSE;
	}
}


clistentry_t *Cmd_FindInCompleteList (char *name)
{
	clistentry_t search = {name};

	// sort the completion list if it's dirty
	Cmd_CheckList ();

	// and search it
	return (clistentry_t *) bsearch (&search, cmd_completelist, cmd_numcompletelist, sizeof (clistentry_t), (sortfunc_t) Cvar_CompleteListSortFunc);
}


void Cmd_AddToCompleteList (char *name, void *data, clisttype_t type)
{
	if (cmd_numcompletelist >= MAX_COMPLETE_LIST) return;

	cmd_completelist[cmd_numcompletelist].name = name;
	cmd_completelist[cmd_numcompletelist].data = data;
	cmd_completelist[cmd_numcompletelist].type = type;

	cmd_numcompletelist++;

	// mark the list as dirty
	cmd_complete_dirty = TRUE;
}


void Cmd_RemoveFromCompleteList (char *name)
{
	clistentry_t *found = NULL;

	if (!cmd_numcompletelist) return;

	if (cmd_numcompletelist == 1)
	{
		// empty the list
		cmd_numcompletelist = 0;
		return;
	}

	if ((found = Cmd_FindInCompleteList (name)) != NULL)
	{
		// one less in the list to remove the last item
		cmd_numcompletelist--;

		// overwrite this name with the last item in the list
		found->name = cmd_completelist[cmd_numcompletelist].name;
		found->data = cmd_completelist[cmd_numcompletelist].data;
		found->type = cmd_completelist[cmd_numcompletelist].type;

		// mark the list as dirty
		cmd_complete_dirty = TRUE;
	}
}


/*
============
Cvar_CompleteVariable
============
*/
char *Cvar_CompleteVariable (key_autotab_t *at, char *partial)
{
	if (partial && partial[0])
	{
		// sort the completion list if it's dirty
		Cmd_CheckList ();

		// and set up the autotab
		if (at->StartMatch)
		{
			// build a full list of all partial matches
			Key_ResetAutoTab (at, FALSE);

			for (int i = 0; i < cmd_numcompletelist; i++)
			{
				if (strnicmp (partial, cmd_completelist[i].name, strlen (partial))) continue;

				Key_AddToAutoTab (at, i);
			}
		}

		if (at->NumMatches)
		{
			return cmd_completelist[Key_RunAutoTab (at)].name;
		}
	}

	return NULL;
}


int RectWidth (const RECT *r)
{
	return r->right - r->left;
}


int RectHeight (const RECT *r)
{
	return r->bottom - r->top;
}


void ParseVector (float *vec, char *s)
{
	char string[128];
	char *v = string;
	char *w = string;

	strcpy (string, s);

	for (int i = 0; i < 3; i++)
	{
		while (*v && *v != ' ')
			v++;

		*v = 0;
		vec[i] = atof (w);
		w = v = v + 1;
	}
}

